import tabulate
import logging

from coin.exchange.base.order_gateway import OrderSide

from coin.exchange.kr_rest.feed.feed_impl import generate_feed_subscriber
from coin.exchange.kr_rest.order.order_impl import generate_order_subscriber


class BalanceContainer(object):
  def __init__(self):
    self.timestamp = None
    self.balance = {}

  def update_balance(self, timestamp, balance_proto):
    for each_balance in balance_proto.each_balance:
      currency = each_balance.currency
      if each_balance.total > 0:
        self.timestamp = timestamp
        self.balance[currency] = each_balance.total

  def get_balance(self, currency):
    return self.balance.get(currency, None)

  def print_balance(self):
    if self.timestamp is None:
      return
    table = []
    for key, bal in sorted(self.balance.items()):
      table.append(['*', key, bal])

    logging.info('\n'
                 '----- Balance:\n* %s\n%s' %
                 (self.timestamp,
                  tabulate.tabulate(table, headers=['*', 'Currency', 'Total'], floatfmt=".6f")))


def get_order_feed_base(market_type, exchange, product, key_file):
  order = generate_order_subscriber(market_type, exchange, None, product, key_file)

  feed = generate_feed_subscriber(market_type, exchange, None, product)
  return order, feed


def order_side_to_sync_side(order_side):
  if order_side == OrderSide.BUY:
    return "bid"
  elif order_side == OrderSide.SELL:
    return "ask"
  else:
    raise ValueError(order_side)


def get_order_price(*,
                    execution_mode,
                    order_side,
                    agg_price_tuple,
                    bbo_price_tuple,
                    payup,
                    max_payup_bps):
  if order_side == OrderSide.BUY:
    if execution_mode == 'agg':
      if (max_payup_bps is None or agg_price_tuple.ask_price <
          (1 + max_payup_bps * 1e-4) * bbo_price_tuple.bid_price):
        return agg_price_tuple.ask_price
      else:
        return None
    elif execution_mode == 'pass':
      if bbo_price_tuple.bid_price + payup < bbo_price_tuple.ask_price:
        return bbo_price_tuple.bid_price + payup
      else:
        return bbo_price_tuple.bid_price
    elif execution_mode == 'pass_deep':
      return bbo_price_tuple.ask_price - payup
    else:
      raise ValueError(execution_mode)
  elif order_side == OrderSide.SELL:
    if execution_mode == 'agg':
      if (max_payup_bps is None or agg_price_tuple.bid_price >
          (1 - max_payup_bps * 1e-4) * bbo_price_tuple.ask_price):
        return agg_price_tuple.bid_price
      else:
        return None
    elif execution_mode == 'pass':
      if bbo_price_tuple.ask_price - payup > bbo_price_tuple.bid_price:
        return bbo_price_tuple.ask_price - payup
      else:
        return bbo_price_tuple.ask_price
    elif execution_mode == 'pass_deep':
      return bbo_price_tuple.bid_price + payup
    else:
      raise ValueError(execution_mode)
  else:
    raise ValueError(order_side)
